package com.pan.kang.core;

import com.pan.kang.executor.TaskExecutors;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Aspect
@Component
public class PostProcessAspect {

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private ApplicationEventPublisher eventPublisher;


    @Pointcut("@annotation(com.pan.kang.core.EnablePostProcess)")
    public void isPostProcessEnabled() {
    }

    @AfterReturning(value = "isPostProcessEnabled()", returning = "result")
    public void executor(JoinPoint joinPoint, Object result) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class<? extends PostProcessPlanProxy> proxyType = signature.getMethod().getAnnotation(EnablePostProcess.class).proxy();
        PostProcessPlanProxy planProxy = applicationContext.getBean(proxyType);
        PostProcessParam.PlanTask planTask = new PostProcessParam.PlanTask(signature.getMethod().getName(),
                joinPoint.getArgs()[0], result);
        List<PostProcessTask> tasks = planProxy.plan(planTask);
        for (PostProcessTask task : tasks) {
            TaskExecutors taskExecutor = TaskExecutors.valueOf(task.getExecutorId());
            Class<? extends PostProcessExecutor> executorType = taskExecutor.getExecutorType();
            task.setPostProcessExecutor(applicationContext.getBean(executorType));
        }
        List<PostProcessTask> syncTasks = tasks.stream().filter(task -> !task.isAsync()).collect(Collectors.toList());
        PostProcessEvent syncEvent = PostProcessEvent.syncEvent(this);
        syncEvent.setTasks(syncTasks);
        eventPublisher.publishEvent(syncEvent);
        List<PostProcessTask> asyncTasks = tasks.stream().filter(PostProcessTask::isAsync).collect(Collectors.toList());
        PostProcessEvent asyncEvent = PostProcessEvent.asyncEvent(this);
        asyncEvent.setTasks(asyncTasks);
        eventPublisher.publishEvent(asyncEvent);
    }
}
